﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.VisualStudio.TeamFoundation;
using Microsoft.VisualStudio.Text.Editor;
using System.ComponentModel;
using FunctionPoint.UIManagers;
using Microsoft.VisualStudio.Shell;
using Microsoft.VisualStudio.TextManager.Interop;
using System.ComponentModel.Composition;
using Microsoft.VisualStudio.Editor;
using Microsoft.VisualStudio.Shell.Interop;
using FunctionPoint.Utility;
using Microsoft.VisualStudio.Utilities;
using System.Diagnostics;
using Microsoft.VisualStudio.TeamFoundation.VersionControl;
using Microsoft.TeamFoundation.VersionControl.Client;
using FCommon = FunctionPoint.Common;
using System.Collections.ObjectModel;
using Microsoft.TeamFoundation.Server;
using Microsoft.TeamFoundation.WorkItemTracking.Client;
using FunctionPoint;
using FunctionPoint.Adornments;
using Microsoft.TeamFoundation.VersionControl.Common;
using System.Windows;

namespace FunctionPoint.Services
{
    [Export(typeof(IWpfTextViewCreationListener))]
    [ContentType("text")]
    [TextViewRole(PredefinedTextViewRoles.Document)]
    public class FunctionPointManagerService : DependencyObject, IFunctionPointManagerService, SFunctionPointManagerService, IWpfTextViewCreationListener
    {

        [Import(typeof(IVsEditorAdaptersFactoryService))]
        IVsEditorAdaptersFactoryService editorFactory { get; set; }

        [Export(typeof(AdornmentLayerDefinition))]
        [Name(FunctionPoint.Common.Constants.VIEWPORT_ADORNMENTLAYERNAME)]
        [Order(After = PredefinedAdornmentLayers.Caret)]
        [TextViewRole(PredefinedTextViewRoles.Document)]
        public AdornmentLayerDefinition editorAdornmentLayer = null;

        #region Events
        //public event FunctionPointTextViewChangedEventHandler FunctionPointTextViewChanged;
        #endregion
        #region Property Backing Variables
        private IVsTextManager _vsTextManager = null;
        private IServiceProvider owningProvider = null;
        private static TeamFoundationServerExt _teamFoundationsServerExt;
        private static EnvDTE80.DTE2 _dte2;

        public FCommon.TfsUserGroup Designers = null;
        public FCommon.TfsUserGroup Engineers = null;
        private ObservableCollection<FCommon.TfsSecurityObject> AllUsersAndGroups = new ObservableCollection<FCommon.TfsSecurityObject>();

        private TfsUtility _tfsUtilities = null;
 
        private FCommon.ObservableWorkItemCollection _selectedWorkItems;
        private FCommon.ObservableWorkItemCollection _allWorkItemsHierarchy;
        #endregion
        #region Properties
        private static EnvDTE80.DTE2 DTE2
        {
            get
            {
                if (_dte2 == null)
                    _dte2 = ServiceProvider.GlobalProvider.GetService(typeof(SDTE)) as EnvDTE80.DTE2;

                return _dte2;
            }
        }
        public static TeamFoundationServerExt TeamFoundationServerExt
        {
            get
            {
                if (_teamFoundationsServerExt == null && DTE2 != null)
                    _teamFoundationsServerExt = DTE2.GetObject("Microsoft.VisualStudio.TeamFoundation.TeamFoundationServerExt") as TeamFoundationServerExt;


                return _teamFoundationsServerExt;
            }
        }
        public TfsUtility TfsUtilities
        {
            get
            {
                // TODO: This may be modified based on how we react when the solution is closed.  should probably think about that..
                if (_tfsUtilities == null)
                {
                    TfsService service = new TfsService(TeamProject.TeamProjectCollection.Uri.ToString());
                    _tfsUtilities = new TfsUtility(service);
                }
                return _tfsUtilities;
            }
        }       
        public FCommon.ObservableWorkItemCollection SelectedWorkItems
        {
            get
            {
                if (_selectedWorkItems == null)
                    _selectedWorkItems = new FCommon.ObservableWorkItemCollection();

                return _selectedWorkItems;
            }

        }

        public static DependencyProperty SelectedWorkItemsProperty = 
            DependencyProperty.Register("SelectedWorkItems", 
            typeof(FCommon.ObservableWorkItemCollection), 
            typeof(FunctionPointManagerService), 
            new PropertyMetadata(new FCommon.ObservableWorkItemCollection()));

        public static DependencyProperty AllQueryWorkItemsHierarchyProperty = 
            DependencyProperty.Register("AllQueryWorkItemsHierarchy", 
            typeof (FCommon.ObservableWorkItemCollection), 
            typeof(FunctionPointManagerService),
            new PropertyMetadata(new FCommon.ObservableWorkItemCollection()));
        


        public FCommon.ObservableWorkItemCollection AllQueryWorkItemsHierarchy
        {
            get
            {
                if (_allWorkItemsHierarchy == null )
                    _allWorkItemsHierarchy = new FCommon.ObservableWorkItemCollection();
                return _allWorkItemsHierarchy;
            }


        }
        
        private static TeamProject TeamProject
        {
            get;
            set;
        }
        private static VersionControlExt _versionControlExt = null;
        public static VersionControlExt VersionControlExt
        {
            get
            {
                if (_versionControlExt == null && DTE2 != null)
                    _versionControlExt = DTE2.GetObject("Microsoft.VisualStudio.TeamFoundation.VersionControl.VersionControlExt");

                return _versionControlExt;
            }
        }


        private IVsTextManager VsTextManager
        {
            get
            {
                return _vsTextManager;
            }
        }
        private static List<IWpfTextView> TextViews
        {
            get;
            set;
        }
        private static List<IFunctionPointManagerServiceSubscriber> ProjectChangedNotificationSubscribers
        {
            get;
            set;
        }
        public static object Synchronized
        {
            get;
            set;
        }
        #endregion
        #region Constructors

        static FunctionPointManagerService()
        {
            Synchronized = new object();
            TextViews = new List<IWpfTextView>();
            ProjectChangedNotificationSubscribers = new List<IFunctionPointManagerServiceSubscriber>();
        }
        public FunctionPointManagerService() :
            this(ServiceProvider.GlobalProvider)
        {
            Debug.WriteLine(
                string.Format("Entered default constructor for FunctionPointManagerService, Hashcode:{0}", this.GetHashCode()));
        }

        public FunctionPointManagerService(IServiceProvider serviceProvider)
        {
            owningProvider = serviceProvider;
            TextViews = new List<IWpfTextView>();

            Handlers.SolutionEventsListener listener = new Handlers.SolutionEventsListener(serviceProvider);

            listener.AfterSolutionLoaded += () => { SolutionEvents_Opened(); };
            listener.BeforeSolutionClosed += () => { SolutionEvents_Closed(); };

        }

        #endregion

        void SolutionEvents_Opened()
        {
            // need to find the proper source control item and then map that isntead of hte projectCOntext changed event handler.

            string solutionName = DTE2.Solution.FullName;
            string solutionPath = System.IO.Path.GetDirectoryName(solutionName);

            if (VersionControlExt != null)
            {

                TeamProject = VersionControlExt.SolutionWorkspace.GetTeamProjectForLocalPath(solutionPath);
                // get all the proper users for this project

                // null is valid value, it will cause it ot clear all the queries
                LoadProjectUsers(TeamProject);
                NotifySubscribers(TeamProject);

            }



        }

        void LoadProjectUsers(TeamProject teamProject)
        {
            if (teamProject != null)
            {
                IGroupSecurityService2 gss = TeamProject.TeamProjectCollection.GetService<IGroupSecurityService2>();
                var appGroups = gss.ListApplicationGroups(teamProject.ArtifactUri.ToString());

                appGroups.ToList().ForEach(appGroup =>
                {
                    switch (appGroup.DisplayName)
                    {
                        //TODO: Nope, don't like my method here either.
                        case "Solution Design":
                            FCommon.TfsUserGroup userGroup = new FCommon.TfsUserGroup(appGroup, gss, true);
                            Designers = userGroup;
                            AllUsersAndGroups.Add(Designers);
                            break;
                        case "App Developers":
                            Engineers = new FCommon.TfsUserGroup(appGroup, gss, true);
                            AllUsersAndGroups.Add(Engineers);
                            break;
                        default:
                            AllUsersAndGroups.Add(new FCommon.TfsUserGroup(appGroup, gss, true));
                            break;

                    }

                    Debug.WriteLine("AppGroupName: {0}, Type:{1} ", appGroup.DisplayName, appGroup.Type);

                });
            }
            else
            {
                Designers.Members.Clear();
                Engineers.Members.Clear();
            }
        }

        private void LoadUserGroup(FCommon.TfsUserGroup group)
        {
            // assumed it will be a group



        }

        void SolutionEvents_Closed()
        {
            NotifySubscribers(null);
        }

        private void NotifySubscribers(TeamProject teamProject)
        {
            lock (Synchronized)
            {
                ProjectChangedNotificationSubscribers.ForEach(subscriber =>
                {
                    subscriber.ProjectContextChanged(this,
                        new TeamFoundationServerProjectContextChangedEventArgs(teamProject));
                });
            }
        }



        #region -------------------- IFunctionPointManagerService -------------------------


        

        private IWpfTextView GetActiveTextView()
        {
            int mustHaveFocus = 1;
            IVsTextManager txtMgr =
               ServiceProvider.GlobalProvider.GetService(typeof(SVsTextManager)) as IVsTextManager;
            IVsTextView vTextView = null;

            txtMgr.GetActiveView(mustHaveFocus, null, out vTextView);

            if (editorFactory != null)
            {
                IWpfTextView textView = editorFactory.GetWpfTextView(vTextView);
                return textView;
            }
            return null;
        }

        public void AddWorkItemToDisplay(Common.WorkItem workItem, bool includeChildren)
        {
            // iterate through the managers and add the element.  This way whenever the item the textview is visible, no additional tasks need to be done, the tool window

            // now we have to find out if the work item has anything related to the page.  
            //perhaps look at the running document list sometime... Dunno.  

            workItem.IsSelected = true;

            if (!SelectedWorkItems.Contains(workItem))
                SelectedWorkItems.Add(workItem);
            
            

            // Each text view is called because of Splitters, Multi-Mon displays etc.  You can undock a window and have 2 visible views.

            RepaintTextViews();

        }
        public void AddWorkItemToDisplay(Common.WorkItem workItem)
        {
            AddWorkItemToDisplay(workItem, false);
        }


        private void NotifySubscribersOfChange()
        {
            ProjectChangedNotificationSubscribers.ForEach(subscriber =>
            {
                subscriber.SelectedWorkItemsChanged(this, TeamProject.TeamProjectCollection.Uri.ToString(), TeamProject.Name);
            });
        }

        private void RepaintTextViews()
        {
            // notify subscribers of the change

            NotifySubscribersOfChange();

            TextViews.ForEach(textView =>
            {
                textView.GetOrCreateManagerForTextView()
                    .RepaintTextViewAdornments(textView.TextSnapshot);
            });
        }

        public void RemoveWorkItemToDisplay(Common.WorkItem workItem, bool includeChildren)
        {
            if (SelectedWorkItems.Contains(workItem))
            {
                workItem.IsSelected = false;
                SelectedWorkItems.Remove(workItem);
            }

            RepaintTextViews();
        }
        public void RemoveWorkItemToDisplay(Common.WorkItem workItem)
        {
            RemoveWorkItemToDisplay(workItem, true);
        }

        public Common.ObservableWorkItemCollection GetWorkItemsAssociatedToView(IWpfTextView textView)
        {

            string filePath = textView.GetFilePath();
            string serverPath = GetServerPath(filePath);


            // query the TFS Work Item Store to see if we have naything associated with this file in the work item field <href c:FunctionPoint.Common.Constants.TFS_FIELD_DESIGNPOINT_FILEPATH />
            // TODO: Need this to be the relative path.  

            string wiql = string.Format("SELECT * FROM WORKITEMS WHERE [{0}] = '{1}'", FunctionPoint.Common.Constants.TFS_FIELD_DESIGNPOINT_FILEPATH, filePath);

            var queryResult = TfsUtilities.TfsService.WorkItemStore.Query(wiql);
            var workItemList = queryResult.Cast<Microsoft.TeamFoundation.WorkItemTracking.Client.WorkItem>();

            Common.ObservableWorkItemCollection workItemCollection = new Common.ObservableWorkItemCollection();

            workItemList.ToList().ForEach(workItem =>
            {
                Common.WorkItem newWorkItem = Utilities.CreateCommonWorkItem(workItem);
                workItemCollection.Add(newWorkItem);
            });
            return workItemCollection;



        }
        public Common.ObservableWorkItemCollection GetWorkItemsAssociatedToView()
        {
            return GetWorkItemsAssociatedToView(
                GetActiveTextView());
        }

        public void RegisterProjectChangedSubscriber(IFunctionPointManagerServiceSubscriber subscriber)
        {
            lock (Synchronized)
            {
                ProjectChangedNotificationSubscribers.Add(subscriber);
            }
        }

        public FCommon.ObservableWorkItemCollection GetWorkItemsForDisplay()
        {
            return SelectedWorkItems;
        }

        #endregion

     


        public FCommon.TfsUser GetCurrentUser()
        {

            FCommon.TfsUser theUser = null;
            AllUsersAndGroups.Cast<FCommon.TfsUserGroup>().ToList().ForEach(group =>
            {
                string domainBasedAccountName = string.Format("{0}\\{1}", Environment.UserDomainName, Environment.UserName);

                //TODO: Nope, I don't know what the f*ck I'm doing here either.
                var aUser = group.Members.FirstOrDefault(o => o.AccountName == domainBasedAccountName);
                if (aUser != null)
                    theUser = aUser;
            });


            return theUser;
        }
        public List<FCommon.TfsUser> GetUsersByGroupName(string groupName)
        {
            if (groupName == "Solution Design")
                return Designers.Members.ToList(); // hack for testing.  

            return new List<FCommon.TfsUser>();
        }

        public List<FCommon.TfsUserGroup> GetUserGroups()
        {
            throw new NotImplementedException();
        }


        public WorkItem CreateDesignPointWorkItem(string designPointTitle, string designComments, string designerName, int parentWorkItemId, string itemPath, int changeSet, int lineNumber, int closestCharacterPosition)
        {
            var dtWorkItem =  TfsUtilities.CreateDesignPointWorkItem(designPointTitle,
                designComments, designerName, parentWorkItemId, itemPath, changeSet, lineNumber, closestCharacterPosition);

            var commonWorkItem = dtWorkItem.CreateCommonWorkItem();

            AddWorkItemToDisplay(commonWorkItem);


            return dtWorkItem;

        }

        public Common.ObservableFlattenedQueryHierarchy GetFlattenedQueryHierarchy(string projectName, bool includeWorkItems = false)
        {
            return TfsUtilities.GetFlattenedQueryHierarchy(projectName, includeWorkItems);
        }

        public void GetQueryWorkItems(FCommon.WorkItemQuery queryItem)
        {
            TfsUtilities.GetQueryWorkItems(queryItem);
        }

        public int GetLatestChangeSetId(string localPath)
        {

            var workspace = TeamProject.VersionControlServer.TryGetWorkspace(localPath);
            if (workspace != null)
            {
                var itemHistory = workspace.VersionControlServer.QueryHistory(GetServerPath(localPath),
                      VersionSpec.Latest, 0,
                      RecursionType.None,
                      null,
                      null,
                      null,
                      3,
                      false,
                      true);

                if (itemHistory != null)
                {
                    var changeSet = itemHistory.Cast<Changeset>().FirstOrDefault();

                    if (changeSet != null)
                        return changeSet.ChangesetId;
                }

            }
            return 0;
        }

        public string GetServerPath(string localPath)
        {
            var workspace = TeamProject.VersionControlServer.TryGetWorkspace(localPath);
            if (workspace != null)
                return workspace.GetServerItemForLocalItem(localPath);

            return localPath;
        }

        public ObservableCollection<FCommon.WorkItemQueryFolder> GetAllQueriesAndOptionallyWorkItems(string projectName, bool includeWorkItems = false, bool flattenHierarchy = false)
        {
            // because we are adding what is likely a new hash coded work item, we have to make sure that previously selected items are included.

            var workItems = TfsUtilities.GetAllQueriesAndOptionallyWorkItems(projectName, includeWorkItems, flattenHierarchy);
            return workItems;
        }

        #region -------------------- IWpfTextViewCreationListener -----------------------

        public void TextViewCreated(IWpfTextView textView)
        {

            textView.GetOrCreateManagerForTextView();

            textView.Closed += new EventHandler((sender, args) =>
            {
                TextViews.Remove(textView);
            });

            TextViews.Add(textView);


        }
        #endregion

    
public event PropertyChangedEventHandler  PropertyChanged;
}
}
